import re
from datetime import timedelta

# python sys info
from sys import version
from sys import version_info as versionInfo
from sys import path as paths
from sys import api_version as cAPIVersion
from sys import platform
from sys import prefix
from sys import exec_prefix as execPrefix
from sys import getrefcount as getRefCount

# garbage collection
from gc import isenabled as gcIsEnabled
from gc import collect as gcCollect
from gc import get_objects as gcObjects
if versionInfo[:3] >= (2,5):
    from gc import get_count as gcCounts
else:
    gcCounts = None
from gc import get_referrers as gcReferrers
from gc import get_referents as gcReferents
from gc import garbage
gcObjectCount = lambda: len(gcObjects())

# object inspection
from inspect import getmembers as getMembers

# resources
import resource

secondsInDay = 60 * 60 * 24.
minutesInDay = 60 * 24.

class TimeDelta(timedelta):

    def asSeconds(self):
        return (self.days*secondsInDay + self.seconds +
            self.microseconds/1000000.)
    asSeconds = property(asSeconds)

    def asMinutes(self):
        return self.asSeconds / 60.
    asMinutes = property(asMinutes)

    def asHours(self):
        return self.asMinutes / 60.
    asHours = property(asHours)

    def asDays(self):
        return self.asHours / 24.
    asDays = property(asDays)

class ResourceUsage(object):

    def __init__(self, data):
        self.timeInUserMode = TimeDelta(data[0]/secondsInDay)
        self.timeInSystemMode = TimeDelta(data[1]/secondsInDay)
        self.timeInBothModes = TimeDelta(self.timeInUserMode.asDays +
            self.timeInSystemMode.asDays)
        self.maxResSize = data[2]
        self.sharedMemSize = data[3]
        self.unsharedMemSize = data[4]
        self.unsharedStackSize = data[5]
        self.pageReclaims = data[6]
        self.swaps = data[7]
        self.blockInputOps = data[8]
        self.blockOutputOps = data[9]
        self.msgsSent = data[10]
        self.msgsReceived = data[11]
        self.sigsReceived = data[12]
        self.volCtxSwitches = data[13]
        self.involCtxSwitches = data[14]

    def data(self):
        return self.__dict__
    data = property(data)

def getRUsageSelf():
    return ResourceUsage(resource.getrusage(resource.RUSAGE_SELF))

def getRUsageChildren():
    return ResourceUsage(resource.getrusage(resource.RUSAGE_CHILDREN))

#def getRUsageBoth():
#    return ResourceUsage(resource.getrusage(resource.RUSAGE_BOTH))

# os-level info
import os
from os import getpid as getPID

class Times(object):

    def __init__(self):
        times = os.times()
        self.user = TimeDelta(times[0]/secondsInDay)
        self.system = TimeDelta(times[1]/secondsInDay)
        self.childrensUser = TimeDelta(times[2]/secondsInDay)
        self.childrensSystem = TimeDelta(times[3]/secondsInDay)
        self.elapsed = TimeDelta(times[4]/secondsInDay)

    def data(self):
        return self.__dict__
    data = property(data)

getTimes = lambda: Times()

class Bytes(object):

    def __init__(self, bytes, unit='b'):
        if unit.lower() == 'kb':
            bytes = bytes * 1024
        elif unit.lower() == 'mb':
            bytes = bytes * 1024 ** 2
        elif unit.lower() == 'gb':
            bytes = bytes * 1024 ** 3
        self.bytes = bytes

    def asKB(self):
        return self.bytes / 1024.
    kb = property(asKB)

    def asMB(self):
        return self.asKB() / 1024.
    mb = property(asMB)

    def asGB(self):
        return self.asMB() / 1024.
    gb = property(asGB)

class ProcessStat(object):

    def __init__(self, process=None):
        self.process = getPID()
        if process != None:
            self.process = process
        if platform == 'darwin':
            self.procResSize = '/proc/%s/task/basic_info/resident_size'
            self.procVirtSize = '/proc/%s/task/basic_info/virtual_size'
            self.units = {'rss': 'kb', 'vsize': 'kb'}
            self.preprocFunc = self._darwinStat
        elif platform.startswith('linux'):
            self.procStat = '/proc/%s/stat'
            self.preprocFunc = self._linuxStat
            self.units = {'rss': 'kb', 'vsize': 'b'}

    def _parseStat(self, data):
        data = data.split(' ')
        return dict(zip(['pid', 'filename', 'state', 'ppid', 'pgrp',
            'session', 'tty_nr', 'tpgid', 'flags', 'minflt', 'cminflt',
            'majflt', 'cmajflt', 'utime', 'stime', 'cutime', 'cstime',
            'priority', 'nice', 'null', 'itreal', 'starttime', 'vsize',
            'rss', 'rlim', 'startcode', 'endcode', 'startstack', 'kstkesp',
            'kstkeip', 'signal', 'blocked', 'sigignore', 'sigcatch',
            'wchan', 'nswap', 'cnswap', 'exit_signal', 'processor',
            'rt_priority', 'policy', 'agg_blkioi'], data))

    def _darwinStat(self):
        rss = self._getProcFileData(self.procResSize % self.process)
        rss = re.split('\s', rss.strip())[0]
        vsize = self._getProcFileData(self.procVirtSize % self.process)
        vsize = re.split('\s', vsize.strip())[0]
        self.data = {'rss': rss, 'vsize': vsize}

    def _linuxStat(self):
        data = self._getProcFileData(self.procStat % self.process)
        self.data = self._parseStat(data)

    def _getProcFileData(self, filename):
        fh = open(filename)
        data = fh.read()
        fh.close()
        return data

    def getResSize(self):
        self.preprocFunc()
        return Bytes(int(self.data['rss']), self.units['rss'])
    resSize = property(getResSize)

    def getVirtSize(self):
        self.preprocFunc()
        return Bytes(int(self.data['vsize']), self.units['vsize'])
    virtSize = property(getVirtSize)

getStat = lambda: ProcessStat()
